// 函数式:闭包与迭代器
pub fn learn_functional() {
  // _learn_closures0();
  //_learn_only_borrows();
  //println!("---");
  //_learn_borrows_mutably();
  //println!("---");
  //_learn_move();
  //println!("---");
  //_learn_sort_by_key();
  _learn_iterator_map();
}

// 闭包:可以捕获定义时所处环境的值的匿名函数变量或参数

#[derive(Debug, PartialEq, Copy, Clone)]
enum ShirtColor {
  Red,
  Blue,
}

struct Inventory {
  shirts: Vec<ShirtColor>,
}

impl Inventory {
  fn giveaway(&self, user_preference: Option<ShirtColor>) -> ShirtColor {
    user_preference.unwrap_or_else(|| self.most_stocked())
  }
  fn most_stocked(&self) -> ShirtColor {
    let mut num_red = 0;
    let mut num_blue = 0;

    for color in &self.shirts {
      match color {
        ShirtColor::Red => num_red += 1,
        ShirtColor::Blue => num_blue += 1,
      }
    }

    if num_red > num_blue {
      ShirtColor::Red
    } else {
      ShirtColor::Blue
    }
  }
}

fn _learn_closures0() {
  let mut store = Inventory {
    shirts: vec![ShirtColor::Blue, ShirtColor::Red, ShirtColor::Blue],
  };
  let user_pref1 = Some(ShirtColor::Red);
  let giveaway1 = store.giveaway(user_pref1);
  println!(
    "The user with preference {:?} gets {:?}",
    user_pref1, giveaway1
  );

  let user_pref2 = None;
  let giveaway2 = store.giveaway(user_pref2);
  println!(
    "The user with preference {:?} gets {:?}",
    user_pref2, giveaway2
  );

  store.shirts.push(ShirtColor::Red);
  store.shirts.push(ShirtColor::Red);

  let user_pref2 = None;
  let giveaway2 = store.giveaway(user_pref2);
  println!(
    "The user with preference {:?} gets {:?}",
    user_pref2, giveaway2
  );
}

// 捕获引用或移动所有权
fn _learn_only_borrows() {
  let list = vec![1, 2, 3];
  println!("Before defining closure:{:?}", list);
  let only_borrows = || println!("From closure:{:?}", list);
  println!("Before calling closure:{:?}", list);
  only_borrows();
  println!("After calling closure:{:?}", list);
}

fn _learn_borrows_mutably() {
  let mut list = vec![1, 2, 3];
  println!("Before defining closure:{:?}", list);
  // 当borrows_mutably定义时,它捕获了list的可变引用.
  let mut borrows_mutably = || list.push(7);
  // 当可变借用存在时不允许有其他的借用,所以在闭包定义和调用之间不能有不可变引用来进行println!.
  // println!("Before calling closure:{:?}", list); // err,can't borrow 'list' as immutable because it's also borrowed as mutable
  borrows_mutably();
  // 闭包在被调用后就不再被使用,这时可变借用结束.

  println!("After calling closure:{:?}", list);
}

// 即使闭包不严格需要所有权,如果希望强制闭包获取它用到的环境中值的所有权,可以在参数列表前使用move关键字.
// 在将闭包传递到一个新的线程时,这个技巧很有用,它可以移动数据所有权给新线程.
use std::thread;
fn _learn_move() {
  let list = vec![1, 2, 3];
  println!("Before defining closure:{:?}", list);
  // 使用move来强制闭包为线程获取list的所有权
  // 没有move就会报错:closure may outlive the current function, but it borrows `list`,
  // which is owned by the current function may outlive borrowed value `list`
  thread::spawn(move || println!("From thread:{:?}", list))
    .join()
    .unwrap();
  // 如果主线程维护了 list 的所有权但却在新线程之前结束并且丢弃了 list，
  // 则在线程中的不可变引用将失效. 因此没有move会编译报错
}

// 将被捕获的值移出闭包和Fn trait
// 一旦闭包捕获了定义它的环境中的一个值的引用或者所有权(如果有,会被移进闭包),闭包体中的代码定义了
// 稍后在闭包计算时对引用或值如何操作(如果有,会被移出闭包).闭包体可以做以下任何事:
// 将一个捕获的值移出闭包,修改捕获的值,即不移动也不修改值,或者一开始就不从环境中捕获值.
// 闭包捕获和处理环境中的值的方式影响闭包实现的trait.Trait是函数和结构体指定它们能用的闭包的类型的方式.
// 闭包自动,渐进地实现一个,两个或三个Fn trait.
// 1, FnOnce 适用于能被调用一次的闭包,所有闭包都至少实现了这个trait,因为所有闭包都能被调用.
// 一个会将捕获的值移出闭包体的闭包只实现FnOnce trait,这是因为它只能被调用一次.
// 2, FnMut 适用于不会将捕获的值移出闭包体的闭包,但它可能会修改被捕获的值.这类闭包可以被调用多次.
// 3, Fn 适用于既不将被捕获的值移出闭包体也不修改被捕获的值的闭包,当然也包括不从环境中捕获值的闭包.
// 这类闭包可以被调用多次而不改变它们的环境.
/*
impl<T> Option<T> {
  pub fn unwrap_or_else<F>(self, f:F)->T
  where
    F: FnOnce()->T
  {
    // 如果有值就返回值,没有就执行最多执行一次的闭包f
    match self {
      Some(x)=>x,
      None=>f(),
    }
  }
}
*/
// 函数也可以实现所有的三种Fn traits.如果我们不需要从环境中捕获值,则可以在需要某种实现了Fn trait的
// 东西时使用函数而不是闭包. 比如在 Option<Vec<T>> 的值上调用unwrap_or_else(Vec::new)

#[derive(Debug)]
struct Rectangle {
  width: u32,
  height: u32,
}

// sort_by_key 使用可变借用
// 这个闭包以一个slice中当前被考虑的元素的引用作为参数,返回一个可以用来排序的K类型的值.
fn _learn_sort_by_key() {
  let mut list = [
    Rectangle {
      width: 10,
      height: 1,
    },
    Rectangle {
      width: 3,
      height: 5,
    },
    Rectangle {
      width: 7,
      height: 12,
    },
  ];
  println!("{:#?}", list);
  // sort_by_key被定义为接收一个FnMut闭包的原因是它会多次调用这个闭包:每个slice中的元素调用一次.
  // 闭包|r|r.width不捕获,修改或将任何东西移出它的环境,所以它满足trait bound的要求.
  list.sort_by_key(|r| r.width);
  println!("{:#?}", list);

  /*
  let mut sort_operations = vec![];
  let value = String::from("by key called");

  list.sort_by_key(|r|{
    // 尝试将捕获到的value移出闭包体:转移value的所有权到一个vec中
    // 此行为决定了这个闭包只能被调用一次,因为再次调用时,value已经不在闭包的环境中了.
    // 因此这个闭包只实现了FnOnce.但sort_by_key定义要求闭包必须实现FnMut,因此编译报错.
    //sort_operations.push(value); // err,can't move out 'value', a captured variable in an 'FnMut' closure
    r.width
  });
  println!("{:#?}", list);
  */
  // 修复如下:
  let mut num_sort_operations = 0;
  list.sort_by_key(|r| {
    num_sort_operations += 1;
    r.width
  });
  println!("{:#?}, sorted in {num_sort_operations} operations", list);
}

// 使用迭代器处理元素序列
// 迭代器模式允许你对一个序列的项进行某些处理. 迭代器(iterator)负责遍历序列中的每一项和决定序列
// 何时结束的逻辑.当使用迭代器时,我们无需重新实现这些逻辑.

// 在rust中,迭代器是惰性的.这意味着在调用方法使用迭代器之前它都不会有效果.
fn _learn_iter_for() {
  let v1 = vec![1, 2, 3];
  let v1_iter = v1.iter();
  for val in v1_iter {
    println!("Got:{}", val);
  }
}

// Iterator trait和next方法
// 迭代器都实现了一个叫做Iterator的定义于标准库的trait.
/*
pub trait Iterator {
  // 这段代码表明实现Iterator trait要求同时定义一个Item类型,
  // 这个Item类型被用作next方法的返回值类型.
  // 即Item类型将是迭代器返回元素的类型.
  type Item;
  fn next(&mut self)->Option<Self::Item>;
  ....
}
*/
#[test]
fn iterator_demonstration() {
  let v1 = vec![1, 2, 3];
  // v1_iter是可变的:在迭代器上调用next方法改变了迭代器中用来记录序列位置的状态.
  // 使用for循环时无需使v1_iter可变是因为 for 循环会获取 v1_iter 的所有权并在后台使 v1_iter 可变.
  let mut v1_iter = v1.iter();
  // 从next调用中得到的值是vector的不可变引用. iter方法生成一个不可变引用的迭代器.
  // 如果需要一个获取v1所有权并返回拥有所有权的迭代器,则可以调用into_iter而不是iter.
  // 如果需要迭代可变引用,则可以调用iter_mut,而不是iter.
  assert_eq!(v1_iter.next(), Some(&1));
  assert_eq!(v1_iter.next(), Some(&2));
  assert_eq!(v1_iter.next(), Some(&3));
  assert_eq!(v1_iter.next(), None);
  println!("2");
}

// 消费迭代器的方法
// 标准库提供了一系列默认实现的方法.一些方法调用了next,这些调用了next方法的方法被称为 消费适配器,
// 因为调用它们会消耗迭代器.如sum
#[test]
fn iterator_sum() {
  let v1 = vec![1, 2, 3];
  let v1_iter = v1.iter();
  let total: i32 = v1_iter.sum();
  assert_eq!(total, 6);
  // 调用sum之后不再允许使用v1_iter. 因为调用sum时它会获取迭代器的所有权.
}

// 产生其他迭代器的方法
// 迭代器适配器(iterator adaptors), 它们允许我们将当前迭代器变为不同类型的迭代器.
// 可以链式调用多个迭代器适配器.不过因为所有迭代器都是惰性的,必须调用一个消费适配器方法以便获取
// 迭代器适配器调用的结果.
fn _learn_iterator_map() {
  let v1: Vec<i32> = vec![1, 2, 3];
  // 调用迭代器适配器map来创建一个新的迭代器
  // 使用collect方法消费迭代器并将结果收集到一个数据结构中
  let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
  println!("{:?}", v2);
}

// 使用捕获其环境的闭包
// 很多迭代器适配器接受闭包作为参数,而通常指定为迭代器适配器参数的闭包会是捕获其环境的闭包
#[derive(PartialEq, Debug)]
struct Shoe {
  size: u32,
  style: String,
}
fn _shoes_in_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
  shoes.into_iter().filter(|s| s.size == shoe_size).collect()
}
#[cfg(test)]
mod tests {
  use super::*;
  #[test]
  fn filters_by_size() {
    let shoes = vec![
      Shoe {
        size: 10,
        style: String::from("sneaker"),
      },
      Shoe {
        size: 13,
        style: String::from("sandal"),
      },
      Shoe {
        size: 10,
        style: String::from("boot"),
      },
    ];
    let in_my_size = _shoes_in_size(shoes, 10);
    assert_eq!(
      in_my_size,
      vec![
        Shoe {
          size: 10,
          style: String::from("sneaker")
        },
        Shoe {
          size: 10,
          style: String::from("boot")
        },
      ]
    );
  }
}
